package org.belkodevelop.user.service;

import org.belkodevelop.user.domain.User;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.belkodevelop.mail.Mail;
import org.belkodevelop.mail.MailTemplate;
import org.belkodevelop.mail.SimpleMailSender;
import org.belkodevelop.role.dao.RoleDAO;
import org.belkodevelop.token.domain.Token;
import org.belkodevelop.token.exception.TokenNotFoundException;
import org.belkodevelop.token.service.TokenService;
import org.belkodevelop.user.dao.UserDAO;
import org.belkodevelop.user.exception.ErrorSendMail;
import org.belkodevelop.user.exception.UserExistsException;
import org.belkodevelop.user.exception.UserNotFoundException;
import org.belkodevelop.userdatails.UserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.MailException;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService{

    private final Log log = LogFactory.getLog(getClass()); 
    
    @Value("${web.token.life_time}")
    private int lifeTimeToken;
    
    @Autowired
    private UserDAO userDAO;
    
    @Autowired
    private RoleDAO roleDAO;
    
    @Autowired
    private TokenService tokenService;
    
    
    @Autowired
    private SaltSource saltSource;
    
    @Autowired
    private PasswordEncoder passwordEncoder; 
    
    @Autowired
    private SimpleMailSender mailSender;
    
    @Autowired
    private MailTemplate registrationMailTemplate;   
    
    @Autowired
    private MailTemplate recoveryPasswordMailTemplate;
    
    @Transactional
    private void addUser(User user) {
        userDAO.saveUser(user);
        UserDetails userDetails = new UserDetails(user);
        String password = userDetails.getPassword();
        Object salt = saltSource.getSalt(userDetails);
        user.setPassword(passwordEncoder.encodePassword(password, salt));
        userDAO.saveUser(user);
    }
    
    @Transactional
    public void registerUser(User user) throws UserExistsException {
        if (userDAO.getUser(user.getLogin()) != null) {
            throw new UserExistsException();
        }
        user.setRole(roleDAO.getRole("ROLE_USER"));
        addUser(user);

        Mail mail = registrationMailTemplate.createMail(user.getEmail());
        mail.addObject("user", user);
        try {
            mailSender.send(mail);
        } catch (MailException ex) {
            log.warn("Error sending mail!", ex);
        }
    }
    
    @Transactional
    public void registerAdmin(User user) throws UserExistsException {
        if (userDAO.getUser(user.getLogin()) != null) {
            throw new UserExistsException();
        }
        user.setRole(roleDAO.getRole("ROLE_ADMIN"));
        addUser(user);
    }

    @Transactional(readOnly = true)
    public List<User> listUsers() {
        return userDAO.listUsers();
    }

    @Transactional
    public void removeUser(long id) {
        userDAO.removeUser(id);
    }
    
    @Transactional(readOnly = true)
    public User getUserByLogin(String login) throws UserNotFoundException {
        User user = userDAO.getUser(login);
        if (user == null) {
            throw new UserNotFoundException();
        }
        return user;
    }
    
    @Transactional(readOnly = true)
    public User getUserById(long id) throws UserNotFoundException {
        User user = userDAO.getUser(id);
        if (user == null) {
            throw new UserNotFoundException();
        }
        return user;
    }
    
    @Transactional
    public void sendRecoveryPasswordMail(String email) throws ErrorSendMail {
        User user = userDAO.getUserByEmail(email);
        if (user != null) {
            Mail mail = recoveryPasswordMailTemplate.createMail(email);
            Token token;
            try {
                token = tokenService.createToken(Long.toString(user.getId()));
            } catch(Exception ex) {
                throw new ErrorSendMail(ex);
            }
            mail.addObject("user", user);
            mail.addObject("token", token);
            try {
                mailSender.send(mail);
            } catch (MailException ex) {
                log.warn("Error sending mail!", ex);
                throw new ErrorSendMail(ex);
            }        
        }
    }
    
    @Transactional
    public boolean checkRecoveryPasswordToken(String login, String tokenValue) {
        try {
            User user = this.getUserByLogin(login);
            return tokenService.checkToken(tokenValue, Long.toString(user.getId()), lifeTimeToken);
        } catch (TokenNotFoundException | UserNotFoundException ex) {
            return false;
        }
    }
    
    @Transactional
    public void changePassword(long userId, String password) {
        User user = userDAO.getUser(userId);
        user.setPassword(password);
        UserDetails userDetails = new UserDetails(user);
        Object salt = saltSource.getSalt(userDetails);
        user.setPassword(passwordEncoder.encodePassword(userDetails.getPassword(), salt));
        userDAO.saveUser(user);
    }
}
